package com.xinqi.common.base.utils.json;

import cn.hutool.core.date.DatePattern;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.TreeNode;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.node.JsonNodeType;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
import com.google.common.collect.Maps;
import com.xinqi.common.base.utils.date.Dates;
import org.apache.commons.lang3.StringUtils;

import javax.annotation.Nullable;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.SimpleTimeZone;

/**
 * JSON 工具类
 *
 * @author linkai
 * @date 2022/11/25
 */
public interface JSON {

    /**
     * 校验 json 是否合法
     *
     * @return 是否为合法的 json 对象字符串
     */
    static boolean isValidObject(String json) {
        if (json == null || json.length() == 0) {
            return false;
        }
        try {
            JsonNode node = Holder.MAPPER.readTree(json);
            return node.isObject();
        } catch (IOException e) {
            // ignored
        }
        return false;
    }

    /**
     * @return null for invalid json
     */
    @Nullable
    static JsonNodeType getJsonType(String json) {
        if (json == null || json.length() == 0) {
            return null;
        }
        try {
            JsonNode node = Holder.MAPPER.readTree(json);
            return node.getNodeType();
        } catch (IOException e) {
            // ignored
        }
        return null;
    }

    /**
     * json 转 Map
     *
     * @param json 字符串
     * @return 返回 Map对象
     */
    static Map<String, Object> toMap(String json) throws IOException {
        return Holder.MAPPER.readValue(json, Holder.TYPE_REF_MAP_STRING_OBJECT);
    }

    /**
     * 对象 转 Map
     *
     * @param obj 对象
     * @return 返回 Map对象
     */
    @SuppressWarnings("unchecked")
    static Map<String, Object> objToMap(Object obj) throws IOException {
        if (Objects.isNull(obj)) {
            return Maps.newHashMap();
        }
        if (obj instanceof Map) {
            return (Map<String, Object>) obj;
        }
        return Holder.MAPPER.readValue(stringify(obj), Holder.TYPE_REF_MAP_STRING_OBJECT);
    }

    static Map<String, String> toMapStr(String json) throws IOException {
        return Holder.MAPPER.readValue(json, Holder.TYPE_REF_MAP_STRING_STRING);
    }

    /**
     * 对象 转 json
     *
     * @param o 对象
     * @return 返回 json 字符串
     */
    static String stringify(Object o) {
        return stringify(o, false);
    }

    /**
     * 对象 转 json
     *
     * @param o      对象
     * @param pretty 是否美化
     * @return 返回 json 字符串
     */
    static String stringify(Object o, boolean pretty) {
        try {
            if (pretty) {
                return Holder.MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(o);
            } else {
                return Holder.MAPPER.writeValueAsString(o);
            }
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * json 转 对象
     *
     * @param json 字符串
     * @param type 对象类型
     * @return 返回对象
     */
    static <T> T parse(String json, Class<T> type) {
        if (StringUtils.isEmpty(json)) {
            return null;
        }
        try {
            return Holder.MAPPER.readValue(json, type);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * json 转 对象
     *
     * @param json 字符串
     * @param type 类型引用
     * @return 返回对象
     */
    static <T> T parse(String json, TypeReference<T> type) {
        if (StringUtils.isEmpty(json)) {
            return null;
        }
        try {
            return Holder.MAPPER.readValue(json, type);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * json 转 List
     *
     * @param json 字符串
     * @param type 列表中对象的类型
     * @return 返回 List列表
     */
    static <T> List<T> parseList(String json, Class<T> type) {
        if (StringUtils.isEmpty(json)) {
            return null;
        }
        try {
            JavaType valueType = Holder.MAPPER.getTypeFactory().constructParametricType(List.class, type);
            return Holder.MAPPER.readValue(json, valueType);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * If {@link ObjectMapper} is not required, use {@link #reader()} or {@link #writer()}
     * instead.
     *
     * @return The ObjectMapper used by {@link JSON}, the returned mapper is
     * immutable,{@link ObjectMapper#copy()} first if you wanner change.
     * @see <a href=http://stackoverflow.com/a/3909846/1870054>How to share
     * ObjectMapper</a>
     */
    static ObjectMapper mapper() {
        return Holder.MAPPER;
    }

    /**
     * @return Shared writer, this is immutable
     */
    static ObjectWriter writer() {
        return Holder.MAPPER.writer();
    }

    /**
     * @return Shared reader, this is immutable
     */
    static ObjectReader reader() {
        return Holder.MAPPER.reader();
    }

    static void initTimeJd8(ObjectMapper objectMapper) {
        // 设置时区
        objectMapper.setTimeZone(SimpleTimeZone.getTimeZone("GMT+8"));
        // 设置 java.util.Date 类型序列化格式
        objectMapper.setDateFormat(new SimpleDateFormat(DatePattern.NORM_DATETIME_PATTERN));
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        // 将 Long 转换为 String
        javaTimeModule.addSerializer(new ToStringSerializer(Long.TYPE));
        javaTimeModule.addSerializer(new ToStringSerializer(Long.class));
        // JDK8 时间处理
        javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(Dates.DATETIME_FORMAT));
        javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(Dates.DATE_FORMAT));
        javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(Dates.TIME_FORMAT));
        javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(Dates.DATETIME_FORMAT));
        javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(Dates.DATE_FORMAT));
        javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(Dates.TIME_FORMAT));
        objectMapper.registerModule(javaTimeModule);
        objectMapper.registerModule(new ParameterNamesModule());
    }

    class Holder {

        private static final ObjectMapper MAPPER;

        private static final TypeReference<Map<String, Object>> TYPE_REF_MAP_STRING_OBJECT = new TypeReference<Map<String, Object>>() {
        };

        private static final TypeReference<Map<String, String>> TYPE_REF_MAP_STRING_STRING = new TypeReference<Map<String, String>>() {
        };

        static {
            ObjectMapper _mapper = new ObjectMapper();
            _mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
            // 当出现 Java 类中未知的属性时不报错，而是忽略此 JSON 字段
            _mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
//            _mapper.configure(SerializationFeature.WRITE_ENUMS_USING_INDEX, true);
            // 枚举类型调用 `toString` 方法进行序列化
            _mapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true);
            JSON.initTimeJd8(_mapper);
            // 查找并注册所有的Modules
//            _mapper.findAndRegisterModules();
            MAPPER = _mapper;
        }

    }

    /**
     * 将节点保存为字符串, {@link com.fasterxml.jackson.annotation.JsonRawValue} 只作用于序列化,
     * 该反序列化器相当于该操作的反向操作. 使用注解
     * {@code @JsonDeserialize(using = JSON.KeepAsJsonDeserializer.class)}
     */
    class JsonRawValueDeserializer extends JsonDeserializer<String> {
        @Override
        public String deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
            TreeNode tree = jp.getCodec().readTree(jp);
            return tree.toString();
        }
    }

}
